/***************************************************************
|	File:		Graphics.cpp
|	Author:		Michael Mozdzierz
|	Date:		05/24/2014
|
***************************************************************/

#include "stdafx.h"
#include "Graphics.h"
#include "Color.h"
#include "Vertex.h"
#include <assert.h>
#include <fstream>
#include "../Math/Matrix.h"

Graphics::Graphics(void)
{
}

Graphics::~Graphics(void)
{
}

int Graphics::Inilitize(HWND hwnd)
{
	// store the hwnd
	this->hwnd = hwnd; 
	
	D3D_FEATURE_LEVEL  FeatureLevelsRequested = D3D_FEATURE_LEVEL_11_0;
	UINT               numLevelsRequested = 1;
	D3D_FEATURE_LEVEL  FeatureLevelsSupported;

	DXGI_SWAP_CHAIN_DESC scd; // swap chain info
	ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));

	// fill swap chain info
	scd.BufferCount = 1;
	scd.BufferDesc.Width = WINDOW_WIDTH;
	scd.BufferDesc.Height = WINDOW_HEIGHT;
	scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
	scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	scd.OutputWindow = hwnd;
	scd.SampleDesc.Count = 4;
	scd.Windowed = true;
	scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;

	
	UINT createionFlags = 0;


	// create device and swap chain
	HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL,
		D3D_DRIVER_TYPE_HARDWARE,
		NULL,
		0,
		&FeatureLevelsRequested,
		numLevelsRequested,
		D3D11_SDK_VERSION,
		&scd,
		&swapchain,
		&device,
		&FeatureLevelsSupported,
		&device_context);
	if (FAILED(hr))
	{
		MessageBox(hwnd, L"Failed to inilitize d3d", L"D3D FAILURE", MB_OK);
		return D3D_INIT_FAILURE;
	}

	// get the address of the backbuffer
	ID3D11Texture2D* back_buffer;
	swapchain->GetBuffer(0, _uuidof(ID3D11Texture2D), (void**)&back_buffer);

	// create the render target
	device->CreateRenderTargetView(back_buffer, nullptr, &this->back_buffer);
	back_buffer->Release();

	// send the back buffer to the device
	device_context->OMSetRenderTargets(1, &this->back_buffer, nullptr);

	// allocate the view port
	D3D11_VIEWPORT viewport;
	memset(&viewport, 0, sizeof(D3D11_VIEWPORT));

	viewport.TopLeftX = 0;
	viewport.TopLeftY = 0;
	viewport.Width = WINDOW_WIDTH;
	viewport.Height = WINDOW_HEIGHT;
	
	device_context->RSSetViewports(1, &viewport);

	// store a value to hold the result of 'InitPipline'
	int piplineResult = InitPipeline();
	if (piplineResult)
		return piplineResult;

	
	
	return 0;
}

void Graphics::DrawVertecies(VertexPositionColor* vBuffer, unsigned int length, Matrix* world, Matrix* view, Matrix* projection)
{
	ID3D11Buffer* vertex_buffer;

	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(D3D11_BUFFER_DESC));

	bd.Usage = D3D11_USAGE_DYNAMIC;
	bd.ByteWidth = sizeof(VertexPositionColor) * 3;
	bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	device->CreateBuffer(&bd, NULL, &vertex_buffer);

	int length_bytes = length * sizeof(VertexPositionColor);

	D3D11_MAPPED_SUBRESOURCE vSubresource;
	device_context->Map(vertex_buffer, 0, D3D11_MAP::D3D11_MAP_WRITE_DISCARD, 0, &vSubresource);
	memcpy(vSubresource.pData, vBuffer, length_bytes);
	device_context->Unmap(vertex_buffer, 0);

	UINT stride = sizeof(VertexPositionColor);

	UINT offset = 0;

	cBufferData.World = *reinterpret_cast<XMFLOAT4X4*>(world);

	// send constant buffer to device
	D3D11_MAPPED_SUBRESOURCE cBuffer_resource;

	device_context->UpdateSubresource(constantBuffer, 0, nullptr, &cBufferData, 4, 4);
		
	device_context->CSSetConstantBuffers(0, 1, &constantBuffer);
	device_context->IASetVertexBuffers(0, 1, &vertex_buffer, &stride, &offset);

	device_context->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	device_context->Draw(3, 0);
}

#ifdef _DEBUG
#include <D3D11.h>
#include <D3DX11.h>
#endif

int Graphics::InitPipeline(void)
{
	const wchar_t* filename = L"content/shaders/defaultShaders.fx";
	// allocate a blank buffer for the vertex shader
	ID3D10Blob* vs;
	// alocate a pointer to comilation errors
	ID3D10Blob* errors;
	// compile vertex shader from file
	HRESULT hr = D3DX11CompileFromFile(filename, 0, 0, "VShader", "vs_4_0", 0, 0, 0, &vs, &errors, 0);
	// break out of this function if compilation failed
	if (FAILED(hr))
	{
		// create a file for error reporting
		fstream file = fstream("errors.txt", ios::out | ios::binary);
		// write error buffer to file
		file.write(reinterpret_cast<const char*>(errors), errors->GetBufferSize());
		// close file
		file.close();
		return COMPILE_SHADER_FAILURE;
	}
	// tell the graphics device to create the vertex shader
	device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), nullptr, &vertex_shader);
	// send the shader to the device
	device_context->VSSetShader(vertex_shader, 0, 0);
	// allocate a blank buffer for the pixel shader
	ID3D10Blob* ps;
	// compile the pixel shader from the file
	hr = D3DX11CompileFromFile(filename, 0, 0, "PShader", "ps_4_0", 0, 0, 0, &ps, 0, 0);	
	// break out of this function if compilation failed
	if (FAILED(hr))
		return COMPILE_SHADER_FAILURE;
	// tell the graphics device to create the pixel shader
	device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), NULL, &pixel_shader);
	// send the shader to the device
	device_context->PSSetShader(pixel_shader, 0, 0);
	// create layout
	D3D11_INPUT_ELEMENT_DESC bufferLayoutDescription[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
	};
	

	device->CreateInputLayout(bufferLayoutDescription, 2, vs->GetBufferPointer(), vs->GetBufferSize(), &buffer_layout);
	// send the buffer layout to the device
	device_context->IASetInputLayout(buffer_layout);

	// create the constant buffer
	D3D11_BUFFER_DESC constantBufferDesc;
	ZeroMemory(&constantBufferDesc, sizeof(D3D11_BUFFER_DESC));

	memset(&constantBufferDesc, 0, sizeof(D3D11_BUFFER_DESC));
	constantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	constantBufferDesc.ByteWidth = sizeof(ConstantBufferData);
	
	D3D11_SUBRESOURCE_DATA sData;
	ZeroMemory(&sData, sizeof(D3D11_SUBRESOURCE_DATA));
	sData.pSysMem = &cBufferData;
	hr = device->CreateBuffer(&constantBufferDesc, &sData, &constantBuffer);
	if (FAILED(hr))
	{
		MessageBox(hwnd, L"Failed to inilitize buffer", L"D3D Buffer failure", MB_OK);
		return D3D_BUFFER_FAILURE;
	}
}

void Graphics::SetVertexShader(const wchar_t* filename)
{
}

void Graphics::SetPixelShader(const wchar_t* filename)
{
}

void Graphics::Present(void)
{
	// send data to the screen
	swapchain->Present(0, 0);
}

void Graphics::Begin(Color& clearColor)
{
	Clear(clearColor);
}

void Graphics::End(void)
{
}

void Graphics::Dispose(void)
{
	// free up d3d resources
	device->Release();
	device_context->Release();
	swapchain->Release();
	back_buffer->Release();
	vertex_shader->Release();
	pixel_shader->Release();
}

void Graphics::Clear(Color& color)
{
	device_context->ClearRenderTargetView(back_buffer, reinterpret_cast<float*>(&color));
}

/*static*/ Graphics* Graphics::GetInstance(void)
{
	static Graphics singleton;

	return &singleton;
}